﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
using Midi;

namespace MIDIRemapper.DeviceInterfaces
{
    [Serializable]
    [XmlRoot("tr")]
    public class Transformable
    {
        #region Properties
        [XmlAttribute("TypeIn")]
        public MidiType _typeIn;
        [XmlAttribute("ProgramIn")]
        public int _programIn;
        [XmlAttribute("ValueIn")]
        public int _valueIn;
        [XmlAttribute("ChannelIn")]
        public int _channelIn;
        [XmlAttribute("TypeOut")]
        public MidiType _typeOut;
        [XmlAttribute("ProgramOut")]
        public int _programOut;
        [XmlAttribute("ValueOut")]
        public int _valueOut;
        [XmlAttribute("ChannelOut")]
        public int _channelOut;

        [XmlAttribute("Transpose")]
        public int _transpose;
        [XmlAttribute("Switch")]
        public bool _switch;
        #endregion

        #region Constructors
        public Transformable(MidiType typeIn,
            MidiType typeOut,
            int channelIn = -1,
            int programIn = -1,
            int valueIn = -1,
            int channelOut = -1,
            int programOut = -1,
            int valueOut = -1,
            int transpose = 0,
            bool switchData = false)
        {
            _typeIn = typeIn;
            _typeOut = typeOut;
            _channelIn = channelIn;
            _channelOut = channelOut;
            _programIn = programIn;
            _programOut = programOut;
            _valueIn = valueIn;
            _valueOut = valueOut;
            _transpose = transpose;
            _switch = switchData;
        }

        public Transformable()
        {
        }
        #endregion

        public bool IsMatch(MidiType type, int channel = -1, int program = -1, int value = -1)
        {
            bool toReturn = false;
            if ((_typeIn == type) &&
                (_channelIn == -1 || channel == -1 || _channelIn == channel) &&
                (_valueIn == -1 || value == -1 || _valueIn == value) &&
                (_programIn == -1 || program == -1 || _programIn == program))
            {
                toReturn = true;
            }
            return toReturn;
        }

        public void GetTransformed(Channel channelIn, int programIn, int valueIn,
            out MidiType typeOut, out Channel channelOut, out int programOut, out int valueOut)
        {
            channelOut = (_channelOut == -1) ? channelIn : (Channel)_channelOut;
            typeOut = _typeOut;
            if (!_switch)
            {
                programOut = Transpose((_programOut == -1) ? programIn : _programOut);
                valueOut = (_valueOut == -1) ? valueIn : _valueOut;
            }
            else
            {
                valueOut = Transpose((_programOut == -1) ? programIn : _programOut);
                programOut = (_valueOut == -1) ? valueIn : _valueOut;
            }
        }

        /// <summary>
        /// Adds _transpose to given value, keeps returned int within
        /// the bounds 0 and 127
        /// </summary>
        private int Transpose(int value)
        {
            int toReturn = value + _transpose;
            if (toReturn > 127)
            {
                return 127;
            }
            if (toReturn < 0)
            {
                return  0;
            }
            return toReturn;
        }

        #region Overrides
        public override bool Equals(object obj)
        {
            if (obj.GetType() == typeof(Transformable))
            {
                Transformable tr = (Transformable)obj;
                if (_typeIn == tr._typeIn &&
                    _typeOut == tr._typeOut &&
                    _channelIn == tr._channelIn &&
                    _channelOut == tr._channelOut &&
                    _programIn == tr._programIn &&
                    _programOut == tr._programOut &&
                    _valueIn == tr._valueIn &&
                    _valueOut == tr._valueOut &&
                    _transpose == tr._transpose)
                {
                    return true;
                }
            }
            return false;
        }

        public override int GetHashCode()
        {
            return (17 +
                _programIn * 23 +
                _programOut * 23 +
                _valueIn * 23 +
                _valueOut * 23 +
                _transpose * 23 +
                (int)_typeIn * 23 +
                (int)_typeOut * 23 +
                _channelIn * 23 +
                _channelOut * 23
                );
        }
        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();

            //Input
            sb.Append(_typeIn.GetUiStringType() + " ");
            if (_programIn != -1)
            {
                if (_typeIn == MidiType.Note_Off || _typeIn == MidiType.Note_On)
                {
                    sb.Append(TypeGetter.GetStringNote(_programIn) + " ");
                }
                else sb.Append(_programIn + " ");
            }
            if (_valueIn != -1)
            {
                sb.Append("(Value: " + _valueIn + ") ");
            }
            if (_channelIn != -1)
            {
                sb.Append("(Channel " + _channelIn + ") ");
            }
            //Output
            
            sb.Append("-> ").Append(_typeOut.GetUiStringType());

            if (_programOut != -1)
            {
                if (_typeOut == MidiType.Note_Off || _typeOut == MidiType.Note_On)
                {
                    sb.Append(" " + TypeGetter.GetStringNote(_programOut));
                }
                else sb.Append(" " + _programOut);
            }
            if (_valueOut != -1)
            {
                sb.Append(" (Value: " + _valueOut + ")");
            }
            if(_transpose != 0)
            {
                sb.Append(" (Transpose: " + _transpose + ")");
            }
            if (_channelOut != -1)
            {
                sb.Append(" (Channel " + _channelOut + ")");
            }

            return sb.ToString();
        }
        #endregion
    }
}